﻿using System;
using System.Linq;
using EPiServer;
using EPiServer.Core;
using PageTypeBuilder;
using TemplateFoundation.Configuration;
using TemplateFoundation.Core;
using TemplateFoundation.Exceptions;
using TemplateFoundation.PageTypes;

namespace TemplateFoundation.Tags
{
    /// <summary>
    /// Represents a single page tag
    /// </summary>
    public class Tag : IEquatable<Tag>
    {
        /// <summary>
        /// Creates a new tag including a reference to its corresponding tag page
        /// </summary>
        /// <param name="name"></param>
        /// <param name="tagPage"></param>
        public Tag(string name, PageReference tagPage)
        {
            // Set name
            Name = name.Trim();

            // Set page link and page count
            if(!PageReference.IsNullOrEmpty(tagPage))
            {
                var page = tagPage.GetPage();
                PageLink = tagPage;
                PageCount = ((ArticleTagListingPageBase)page).PageCount;
                ID = page.PageLink.ID;
                Description = page.Description;
                UrlSegment = page.URLSegment;
            }
        }

        /// <summary>
        /// Creates a new tag including a reference to its corresponding tag page
        /// </summary>
        /// <param name="name"></param>
        /// <param name="tagPage"></param>
        public Tag(string name, ArticleTagListingPageBase tagPage)
        {
            // Set name
            Name = name.Trim();

            // Set page link and page count
            PageLink = tagPage.PageLink;

            try
            {
                PageCount = tagPage.PageCount;
            }
            catch (NullReferenceException)
            {
                // This condition most likely occured because an existing website was integrated with ETF meaning existing tag pages don't have the PageCount property set
                // Remedy this by counting the number of pages that have been tagged with the tag

                var count = PageFactory.Instance.GetArticlesByTag(new Tag(name)).Count;

                if(count>0)
                {
                    PageCount = count;
                }
            }
            
            ID = tagPage.PageLink.ID;
            Description = tagPage.Description;
            UrlSegment = tagPage.URLSegment;
        }

        /// <summary>
        /// Used to retrieve a Tag object even if the associated tag page is missing, or if its PageCount property hasn't been set
        /// </summary>
        /// <param name="name"></param>
        /// <param name="skipInitialization">Used to create a Tag object without initializing the associated tag page and meta data</param>
        /// <returns></returns>
        /// <remarks>Can be used when adding tags to a tag collection, only the tag name is saved in TagCollection properties</remarks>
        public Tag(string name, bool skipInitialization)
        {
            // Set name
            Name = name.Trim();

            if (!skipInitialization)
            {
                if (PageReference.IsNullOrEmpty(Settings.Instance.TagsContainer))
                {
                    throw new TemplateFoundationException("No tags container set, new tags cannot be created until the 'Tags container' site setting has been specified");
                }

                if (!Settings.Instance.TagPageTypeId.HasValue)
                {
                    throw new TemplateFoundationException("No page type for tags specified in site settings");
                }

                // Set name
                Name = name.Trim();

                // Set page link and page count
                var tagPageCriterias = new PropertyCriteriaCollection
                                       {
                                           new PropertyCriteria
                                               {
                                                   Name = "PageName",
                                                   Value = name,
                                                   Type = PropertyDataType.String,
                                                   Required = true
                                               },
                                            new PropertyCriteria
                                                {
                                                    Name = "PageTypeID",
                                                    Value = Settings.Instance.TagPageTypeId.Value.ToString(),
                                                    Type = PropertyDataType.PageType,
                                                    Required = true
                                                }
                                       };

                var result = DataFactory.Instance.FindPagesWithCriteria(Settings.Instance.TagsContainer, tagPageCriterias).FirstOrDefault();

                if (result != null)
                {
                    ArticleTagListingPageBase tagPage;

                    // HACK Explicit PTB casting required because of CMS 6 R2 issue: http://pagetypebuilder.codeplex.com/workitem/8171
                    if (!(result is TypedPageData))
                    {
                        tagPage = (ArticleTagListingPageBase)PageTypeResolver.Instance.ConvertToTyped(result);
                    }
                    else
                    {
                        tagPage = (ArticleTagListingPageBase) result;
                    }

                    PageLink = tagPage.PageLink;

                    try
                    {
                        PageCount = tagPage.PageCount;
                    }
                    catch (NullReferenceException)
                    {
                        // This can happen when an existing website is combined with ETF; often in these cases existing tag pages have not had the PageCount property set
                        PageCount = PageFactory.Instance.GetArticlesByTag(new Tag(name, true)).Count;
                    }

                    ID = tagPage.PageLink.ID;
                    Description = tagPage.Description;
                    UrlSegment = tagPage.URLSegment;
                }
                else
                {
                    PageLink = PageReference.EmptyReference;
                }
            }
        }

        /// <summary>
        /// Creates a new Tag object which looks up the corresponding tag page
        /// </summary>
        /// <param name="name"></param>
        public Tag(string name) : this(name,false)
        {

        }

        #region Properties

        /// <summary>
        /// Gets the name of the tag
        /// </summary>
        public string Name { get; protected set; }

        /// <summary>
        /// Gets the description of the tag
        /// </summary>
        public string Description { get; protected set; }

        /// <summary>
        /// Gets the URL for the associated tag page
        /// </summary>
        public PageReference PageLink { get; protected set; }

        /// <summary>
        /// Gets the number of pages tagged with this tag
        /// </summary>
        public int PageCount { get; protected set; }

        /// <summary>
        /// Gets the ID of the tag
        /// </summary>
        public int ID { get; protected set; }

        /// <summary>
        /// Gets or sets the URL segment.
        /// </summary>
        /// <value>The URL segment.</value>
        public string UrlSegment { get; protected set; }

        #endregion

        #region IEquatable<Tag> Members

        /// <summary>
        /// Returns true if two tags are the same based on their names
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(Tag other)
        {
            return Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase);
        }

        #endregion
    }
}
